package com.kf.bigbluebook.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kf.bigbluebook.common.DeleteRequest;
import com.kf.bigbluebook.common.ErrorCode;
import com.kf.bigbluebook.constant.TeamStatus;
import com.kf.bigbluebook.constant.UserConstant;
import com.kf.bigbluebook.exception.BusinessException;
import com.kf.bigbluebook.model.dto.TeamQuery;
import com.kf.bigbluebook.model.po.Team;
import com.kf.bigbluebook.model.po.User;
import com.kf.bigbluebook.model.po.UserTeam;
import com.kf.bigbluebook.model.request.TeamJoinRequest;
import com.kf.bigbluebook.model.request.TeamQuitRequest;
import com.kf.bigbluebook.model.request.TeamUpdateRequest;
import com.kf.bigbluebook.model.vo.TeamUserVO;
import com.kf.bigbluebook.model.vo.UserVO;
import com.kf.bigbluebook.service.TeamService;
import com.kf.bigbluebook.mapper.TeamMapper;
import com.kf.bigbluebook.service.UserService;
import com.kf.bigbluebook.service.UserTeamService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 86157
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2025-02-10 14:04:49
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {
    @Autowired
    private UserService userService;

    @Autowired
    private UserTeamService userTeamService;
    @Autowired
    private RedissonClient redissonClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long addTeam(Team team, User loginUser) {
        //参数是否为空
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        //是否登录
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        if (!loginUser.getId().equals(team.getUserId())){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"创建队伍用户id与登录用户id不一致");
        }

        //队伍人数大于1，小于等于20
        Integer maxNum = team.getMaxNum();
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍人数错误");
        }
        //队伍标题长度小于20
        String teamName = team.getName();
        if (teamName.length() > 20 || StringUtils.isBlank(teamName)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍标题错误");
        }
        //队伍描述长度小于512
        String teamDescription = team.getDescription();
        if (teamDescription.length() > 512 && StringUtils.isNotBlank(teamDescription)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍描述过长");
        }
        //状态是否位公开,不传默认为公开
        Integer teamStatus = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatus status = TeamStatus.getEnumByValue(teamStatus);
        if (status == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍状态错误");
        }
        //如果为加密，必须要有密码且密码长度小于32位
        String password = team.getPassword();
        if (TeamStatus.SECRET.equals(status)) {
            if (password.length() > 32 || StringUtils.isBlank(password)) {
                throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍密码错误");
            }
        }
        //超时时间要大于当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "超时时间错误");
        }
        //用户最多创建5个队伍
        //todo 可能同时创建大于5个队伍
        Long userId = loginUser.getId();
        QueryWrapper<Team> queryWrapper = new QueryWrapper<Team>();
        queryWrapper.eq("userId", userId);
        long count = this.count(queryWrapper);
        if (count >= 5) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "最多创建5个队伍");
        }
        //插入队伍表
        team.setCreateTime(new Date());
        this.save(team);
        //插入用户队伍关系表
        Long teamId = team.getId();
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        userTeam.setCreateTime(new Date());
        userTeamService.save(userTeam);
        return teamId;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, HttpServletRequest request) {
        if (teamQuery == null || request == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        Long id = teamQuery.getId();
        List<Long> idList = teamQuery.getIdList();
        String name = teamQuery.getName();
        String description = teamQuery.getDescription();
        String searchText = teamQuery.getSearchText();
        Integer maxNum = teamQuery.getMaxNum();
        Integer status = teamQuery.getStatus();
        Long userId = teamQuery.getUserId();
        //获取查询状态参数
        TeamStatus statusEnum = TeamStatus.getEnumByValue(status);
        if (statusEnum == null) {
            statusEnum = TeamStatus.PUBLIC;
        }
        if (!userService.isAdmin(request) && statusEnum.equals(TeamStatus.PRIVATE)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        List<Team> teamList = this.lambdaQuery()
                .eq(id != null && id > 0, Team::getId, id)
                .in(idList != null && !idList.isEmpty(), Team::getId, idList) // 确保 idList 不为空且至少有一个元素
                .like(StringUtils.isNotBlank(name), Team::getName, name)
                .like(StringUtils.isNotBlank(description), Team::getDescription, description) // 确保 searchText 是正确的参数
                .eq(maxNum != null && maxNum > 0, Team::getMaxNum, maxNum)
                .eq(Team::getStatus, statusEnum.getValue())
                .eq(userId != null && userId > 0, Team::getUserId, userId)
                .gt(Team::getExpireTime, new Date())
                .isNotNull(Team::getExpireTime) // 确保 expireTime 不为空
                .list();
        //查询队伍创建人的信息
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teamList) {
            if (team.getUserId() == null) {
                continue;
            }
            User user = userService.getById(team.getUserId());
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            User safetyUser = userService.getSafetyUser(user);
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(safetyUser, userVO);
            teamUserVO.setCreateUser(userVO);
            teamUserVOList.add(teamUserVO);
        }
        //判断用户是否在队伍中
        final List<Long> teamIds = teamUserVOList.stream().map(TeamUserVO::getId).collect(Collectors.toList());
        User loginUser = userService.getLoginUser(request);
        //获取已加入的队伍id
        List<UserTeam> joinUserTeamList = userTeamService.lambdaQuery()
                .eq(UserTeam::getUserId, loginUser.getId())
                .in(!teamIds.isEmpty(),UserTeam::getTeamId, teamIds)
                .list();
        Set<Long> joinTeamIds = joinUserTeamList.stream().map(UserTeam::getTeamId).collect(Collectors.toSet());
        for (TeamUserVO teamUserVO : teamUserVOList) {
            boolean isJoin = joinTeamIds.contains(teamUserVO.getId());
            teamUserVO.setHasJoin(isJoin);
        }
        //查询已加入队伍人数
        List<UserTeam> userTeamList = userTeamService.lambdaQuery().in(!teamIds.isEmpty(),UserTeam::getTeamId, teamIds).list();
        Map<Long, List<UserTeam>> teamToUsers = userTeamList.stream().collect(Collectors.groupingBy(UserTeam::getTeamId));
        for (TeamUserVO teamUserVO : teamUserVOList) {
            int number = teamToUsers.getOrDefault(teamUserVO.getId(), new ArrayList<>()).size();
            teamUserVO.setHasJoinNum(number);
        }
        return teamUserVOList;
    }

    @Override
    public Boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        if (teamUpdateRequest == null || loginUser == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //1、查询队伍是否存在
        Long teamId = teamUpdateRequest.getId();
        if (teamId == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍id不能为空");
        }
        Team team = this.lambdaQuery().eq(Team::getId, teamId).one();
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "该队伍不存在");
        }
        //2、只有管理员或者队伍创建者可以修改
        if (!userService.isAdmin(loginUser) && !team.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH, "权限不足");
        }
        //3、如果队伍状态改为加密，必须要有密码
        if (TeamStatus.SECRET.getValue() == teamUpdateRequest.getStatus() && StringUtils.isBlank(teamUpdateRequest.getPassword())) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "改为加密密码不能为空");
        }
        //4、过期时间必须晚于当前时间
        if (teamUpdateRequest.getExpireTime() != null && new Date().after(teamUpdateRequest.getExpireTime())) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "过期时间必须晚于当前时间");
        }
        return this.lambdaUpdate()
                .eq(Team::getId, teamUpdateRequest.getId()) // 根据 ID 更新
                .set(StringUtils.isNotBlank(teamUpdateRequest.getName()), Team::getName, teamUpdateRequest.getName()) // 更新队伍名称
                .set(StringUtils.isNotBlank(teamUpdateRequest.getDescription()), Team::getDescription, teamUpdateRequest.getDescription()) // 更新描述
                .set(teamUpdateRequest.getExpireTime() != null, Team::getExpireTime, teamUpdateRequest.getExpireTime()) // 更新过期时间
                .set(teamUpdateRequest.getStatus() != null, Team::getStatus, teamUpdateRequest.getStatus()) // 更新状态
                .set(StringUtils.isNotBlank(teamUpdateRequest.getPassword()), Team::getPassword, teamUpdateRequest.getPassword()) // 更新密码
                .update();
    }

    @Override
    public Boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        if (teamJoinRequest == null || loginUser == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "请求参数为空");
        }
        //根据队伍id获取队伍信息
        Team team = this.getById(teamJoinRequest.getTeamId());
        //判断队伍是否存在
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍不存在");
        }
        //判断队伍是否过期
        if (team.getExpireTime().before(new Date())) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "队伍已过期");
        }
        //判断队伍是否为私有
        if (TeamStatus.PRIVATE.getValue() == team.getStatus()) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "无法加入私有队伍");
        }
        //判断队伍密码是否正确
        if (TeamStatus.SECRET.getValue() == team.getStatus() && !team.getPassword().equals(teamJoinRequest.getPassword())) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "密码错误");
        }
        //使用锁防止多个用户同时加入一个队伍，导致人数超过上限
        RLock lock = redissonClient.getLock("kf:bigbluebook:join_team");
        //加入队伍
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    System.out.println("lock:" + Thread.currentThread().getId());
                    //判断用户是否已经加入5个队伍
                    Long count = userTeamService.lambdaQuery().eq(UserTeam::getUserId, loginUser.getId()).count();
                    if (count >= 5) {
                        throw new BusinessException(ErrorCode.PARAM_ERROR, "最多只能加入5个队伍");
                    }
                    //判断用户是否已经加入队伍
                    List<UserTeam> userTeamList = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, team.getId()).eq(UserTeam::getUserId, loginUser.getId()).list();
                    if (!userTeamList.isEmpty()) {
                        throw new BusinessException(ErrorCode.PARAM_ERROR, "您已加入该队");
                    }
                    //判断队伍人数是否已满
                    Long number = getTeamNumber(team.getId());
                    if (number >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAM_ERROR, "该队伍人数已满");
                    }
                    //修改用户队伍表
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(team.getId());
                    userTeam.setUserId(loginUser.getId());
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("unlock:" + Thread.currentThread().getId());
            lock.unlock(); //释放锁
        }
    }

    /**
     * 退出队伍
     * @param teamQuitRequest
     * @param loginUser
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        if (teamQuitRequest == null || loginUser == null){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"参数为空");
        }
        Team team = getTeamById(teamQuitRequest.getTeamId());
        Long teamId = teamQuitRequest.getTeamId();
        //判断用户是否加入队伍
        List<UserTeam> userTeamList = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, teamId)
                .eq(UserTeam::getUserId, loginUser.getId())
                .list();
        if (userTeamList.isEmpty()){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"您未加入该队");
        }
        //队伍只剩一人，解散队伍
        Long number = getTeamNumber(teamId);
        if (number == 1){
            this.removeById(teamId);
        }else {
            //若为队长退出
            if (team.getUserId().equals(loginUser.getId())){
                //将下一个人设为队长
                List<UserTeam> joinTeamList = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, teamId)
                        .orderByAsc(UserTeam::getId)
                        .last("limit 2")
                        .list();
                if (CollectionUtils.isEmpty(joinTeamList) || joinTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam userTeam = joinTeamList.get(1);
                Long userId = userTeam.getUserId();
                boolean result = this.lambdaUpdate().set(Team::getUserId, userId)
                        .eq(Team::getId, teamId)
                        .update();
                if (!result){
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队伍队长失败");
                }
            }
        }
        return userTeamService.lambdaUpdate().eq(UserTeam::getUserId, loginUser.getId())
                .eq(UserTeam::getTeamId, teamId)
                .remove();
    }

    @Override
    public Boolean deleteTeam(DeleteRequest deleteRequest, User loginUser) {
        //校验参数信息
        if (deleteRequest == null){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"参数为空");
        }
        //判断队伍是否存在
        long teamId = deleteRequest.getId();
        Team team = this.getTeamById(teamId);
        //判断是否为队长
        if (!team.getUserId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NO_AUTH,"不能删除非本人创建的队伍");
        }
        //删除关联信息
        userTeamService.lambdaUpdate().eq(UserTeam::getTeamId, teamId).remove();
        return this.removeById(teamId);
    }

    /**
     * 获取加入队伍的人数
     * @param teamId
     * @return
     */
    public Long getTeamNumber(Long teamId) {
        return userTeamService.lambdaQuery().eq(UserTeam::getTeamId, teamId).count();
    }

    public Team getTeamById(Long teamId){
        if (teamId == null || teamId < 0){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"队伍id错误");
        }
        Team team = this.getById(teamId);
        if (team == null){
            throw new BusinessException(ErrorCode.PARAM_ERROR,"该队不存在");
        }
        return team;
    }
}




